#!/bin/sh
#! -*-perl-*-

# Copyright (C) 2012-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This file is offered as-is, without any warranty.
#
# Written by Jim Meyering

# This prologue allows running a perl script as an executable
# on systems that are compliant to a POSIX version before POSIX:2017.
# On such systems, the usual invocation of an executable through execlp()
# or execvp() fails with ENOEXEC if it is a script that does not start
# with a #! line.  The script interpreter mentioned in the #! line has
# to be /bin/sh, because on GuixSD systems that is the only program that
# has a fixed file name.  The second line is essential for perl and is
# also useful for editing this file in Emacs.  The next two lines below
# are valid code in both sh and perl.  When executed by sh, they re-execute
# the script through the perl program found in $PATH.  The '-x' option
# is essential as well; without it, perl would re-execute the script
# through /bin/sh.  When executed by perl, the next two lines are a no-op.
eval 'exec perl -wSx "$0" "$@"'
     if 0;

my $VERSION = '2025-06-10 02:42'; # UTC
# The definition above must lie within the first 8 lines in order
# for the Emacs time-stamp write hook (at end) to update it.
# If you change this file with Emacs, please let the write hook
# do its job.  Otherwise, update this string manually.

# Overview
# ========
#
# Automake is usually used with a separate Makefile.am in each subdirectory
# that contains sources.  The advantage is that the build infrastructure of
# the package is composable.
#
# An alternative way to use Automake is to have a Makefile.am at the top-level
# only, and each subdirectory contains a Makefile.am fragment, called
# <something>.mk, that is included by the top-level Makefile.am.
# This is called "non-recursive Automake" and is explained in
# <https://autotools.info/automake/nonrecursive.html>.  This way makes it
# easier to use a library in one subdirectory from sources in another
# subdirectory, and still have 'make' check dependencies in an optimal way.
#
# The "non-recursive Automake" is activated through the Automake option
# 'subdir-objects'.
#
# There are two ways to support this mode with Gnulib:
#   (a) The gnulib-tool option --automake-subdir.
#   (b) The deprecated 'non-recursive-gnulib-prefix-hack' module.
# Both make use of this script.  This script rewrites the a Makefile.am portion
# made for the usual way, so that it can be used in the non-recursive way.
#
# This script can be invoked in two ways:
#   (a) Through an invocation of build-aux/prefix-gnulib-mk from within
#       gnulib-tool, with options such as
#         --from-gnulib-tool --lib-name=libgnu --prefix=lib/
#   (b) Through an invocation of build-aux/prefix-gnulib-mk from the package's
#       build system (deprecated).
#
# The official way to write Makefile.am snippets that supports both ways is
# through the %reldir% token, that is explained in
# <https://www.gnu.org/software/automake/manual/html_node/Include.html>.
# Doing this would, however, lead to hundreds of occurrences of %reldir%,
# which is not pretty to work with.  Therefore, what Gnulib does is:
#   - This script adds the prefix (e.g. 'lib/') in most places, based on
#     simple patterns (e.g. before source file names and after $(MKDIR_P)).
#   - In other places, that are not covered by this script, we use %reldir%
#     explicitly.

use strict;
use IO::File;
use Getopt::Long;
use File::Basename; # for dirname

(my $ME = $0) =~ s|.*/||;

my $prefix;
my $canon_prefix;
my $lib_name;

sub usage ($)
{
  my ($exit_code) = @_;
  my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
  if ($exit_code != 0)
    {
      print $STREAM "Try '$ME --help' for more information.\n";
    }
  else
    {
      print $STREAM <<EOF;
Usage: $ME --lib-name=NAME FILE  (deprecated)
   or: $ME [--help|--version]
Rewrite a gnulib-tool-generated FILE like lib/gnulib.mk to work with
automake's subdir-objects.

OPTIONS:

This option must be specified:

   --lib-name=NAME    library name, often "lib\$project"

The following are optional:

   --help             display this help and exit
   --version          output version information and exit

EOF
    }
  exit $exit_code;
}

# contents_of_file ($FILE_NAME)
# -----------------------------
sub contents_of_file ($)
{
  my ($file) = @_;
  local $/;                     # Turn on slurp-mode.
  my $f = new IO::File "< $file" or die "$file";
  my $contents = $f->getline or die "$file";
  $f->close;
  return $contents;
}

# contents_of_stdin
# -----------------
sub contents_of_stdin ()
{
  local $/;                     # Turn on slurp-mode.
  my $contents = <STDIN>;
  return $contents;
}

# prefix_word ($WORD)
# -------------------
# Do not prefix special words such as variable dereferences.  Also,
# "Makefile" is really "Makefile", since precisely there is no
# lib/Makefile.
sub prefix_word ($)
{
  local ($_) = @_;
  $_ = $prefix . $_
    unless (/^-/ || m{^\$\(\w+\)} || $_ eq "Makefile" || $_ eq '\\'
            || $_ eq '@ALLOCA@');
  return $_;
}


# prefix_words ($TEXT)
# --------------------
sub prefix_words ($)
{
  local ($_) = @_;
  s{(\S+)}{prefix_word($1)}gem;
  return $_;
}


# prefix_assignment ($LHS-AND-ASSIGN-OP, $RHS)
# --------------------------------------------
sub prefix_assignment ($$)
{
  my ($lhs_and_assign_op, $rhs) = @_;

  # Some variables are initialized by gnulib.mk, and we don't want
  # that.  Change '=' to '+='.
  if ($lhs_and_assign_op =~ /^(GPERF|V_GPERF.*) =$/)
    {
      # Do not change the RHS, which specifies the GPERF program.
    }
  # Don't change variables such as HAVE_INCLUDE_NEXT or SED_HEADER_STDOUT.
  elsif ($lhs_and_assign_op =~ /^(HAVE_|SED_HEADER_)/)
    {
    }
  elsif ($lhs_and_assign_op =~
      /^(SUBDIRS|EXTRA_DIST|BUILT_SOURCES|SUFFIXES|MOSTLYCLEANFILES
         |CLEANFILES|DISTCLEANFILES|MAINTAINERCLEANFILES
         |AM_GNU_GETTEXT)\ =/x)
    {
      $lhs_and_assign_op =~ s/=/+=/;
    }
  # We don't want things such as AM_CPPFLAGS +=
  # -DDEFAULT_TEXT_DOMAIN=\"bison-gnulib\" to apply to the whole
  # Makefile.in: scope it to the library: libbison_a_CPPFLAGS =
  # $(AM_CPPFLAGS) -DDEFAULT_TEXT_DOMAIN=\"bison-gnulib\".
  elsif ($lhs_and_assign_op =~
      /^(AM_CFLAGS|AM_CPPFLAGS)\ \+?=/x)
    {
      $lhs_and_assign_op =~ s/^AM_(\w+)\ \+?=/${lib_name}_a_$1 =/;
      $rhs = " \$(AM_$1)$rhs";
    }
  # We don't want to inherit gnulib's AUTOMAKE_OPTIONS, comment them.
  elsif ($lhs_and_assign_op =~ /^AUTOMAKE_OPTIONS =/)
    {
      $lhs_and_assign_op =~ s/^/# /;
    }
  # Elide any SUFFIXES assignment or concatenation.
  elsif ($lhs_and_assign_op =~ /^SUFFIXES /)
    {
      $lhs_and_assign_op =~ s/^/# /;
    }
  # The words are (probably) paths to files in lib/: prefix them.
  else
    {
      $rhs = prefix_words($rhs)
    }

  # Variables whose name depend on the location: libbison_a_SOURCES =>
  # lib_libbison_a_SOURCES.
  $lhs_and_assign_op =~ s/($lib_name)/$canon_prefix$1/g;

  $lhs_and_assign_op . $rhs;
}

# prefix $CONTENTS
# ----------------
# $CONTENTS is a Makefile content.  Post-process it so that each file-name
# is prefixed with $prefix (e.g., "lib/").
#
# Relies heavily on the regularity of the file generated by gnulib-tool.
sub prefix ($)
{
  # Work on $_.
  local ($_) = @_;

  # $canon_prefix is derived from $prefix in the same way as Automake
  # derives %canon_reldir% from %reldir%. See
  # <https://www.gnu.org/software/automake/manual/html_node/Include.html>.
  $canon_prefix = $prefix;
  $canon_prefix =~ s/[^a-zA-Z0-9_]/_/g;

  # Prefix all the occurrence of files in rules.  If there is nothing
  # after in the :, it's probably a phony target, or a suffix rule.
  # Don't touch it.
  s{^([-\w+/]+\.[-\w.]+ *: *\S.*)$}
   {prefix_words($1)}gem;

  # Prefix files in variables.
  s{^([\w.]+\s*\+?=)(.*)$}
   {prefix_assignment($1, $2)}gem;

  # $(srcdir)/ is actually $(top_srcdir)/$prefix/.
  # The trailing slash is required to avoid matching this rule:
  #   test '$(srcdir)' = . || rm -f $(top_builddir)/GNUmakefile
  s{\$\(srcdir\)/}{\$(top_srcdir)/$prefix}g;

  # Some AC_SUBST patterns remain and would better be Make macros.
  s{\@(MKDIR_P)\@}{\$($1)}g;

  # Adjust paths in mkdir.
  s{(\$\(MKDIR_P\))\s*(\w+)}{$1 $prefix$2}g;

  return $_;
}

{
  my $from_gnulib_tool = 0;

  GetOptions
    (
     'lib-name=s' => \$lib_name,
     help => sub { usage 0 },
     version => sub { print "$ME version $VERSION\n"; exit },
     # Undocumented options:
     'from-gnulib-tool' => \$from_gnulib_tool,
     'prefix=s' => \$prefix,
    ) or usage 1;

  my $fail = 0;
  defined $lib_name
    or (warn "$ME: no library name; use --lib-name=NAME\n"), $fail = 1;

  if ($from_gnulib_tool != 0)
    {
      0 < @ARGV
        and (warn "$ME: too many arguments:\n", join ("\n", @ARGV), "\n"),
          $fail = 1;
      $fail
        and usage 1;

      my $contents = contents_of_stdin ();
      $contents = prefix ($contents);
      print STDOUT $contents;
    }
  else
    {
      # There must be exactly one argument.
      @ARGV == 0
        and (warn "$ME: missing FILE argument\n"), $fail = 1;
      1 < @ARGV
        and (warn "$ME: too many arguments:\n", join ("\n", @ARGV), "\n"),
          $fail = 1;
      $fail
        and usage 1;

      my $file = $ARGV[0];
      $prefix = (dirname $file) . '/';
      warn "prefix=$prefix\n";

      my ($bak) = "$file.bak";
      rename ($file, $bak) or die "$ME: rename $file $bak failed: $!\n";
      my $contents = contents_of_file ($bak);
      $contents = prefix ($contents);
      my $out = new IO::File(">$file")
        or die "$ME: $file: failed to open for writing: $!\n";
      print $out $contents;
    }
}

### Setup "GNU" style for perl-mode and cperl-mode.
## Local Variables:
## perl-indent-level: 2
## perl-continued-statement-offset: 2
## perl-continued-brace-offset: 0
## perl-brace-offset: 0
## perl-brace-imaginary-offset: 0
## perl-label-offset: -2
## cperl-indent-level: 2
## cperl-brace-offset: 0
## cperl-continued-brace-offset: 0
## cperl-label-offset: -2
## cperl-extra-newline-before-brace: t
## cperl-merge-trailing-else: nil
## cperl-continued-statement-offset: 2
## eval: (add-hook 'before-save-hook 'time-stamp nil t)
## time-stamp-line-limit: 50
## time-stamp-start: "my $VERSION = '"
## time-stamp-format: "%Y-%02m-%02d %02H:%02M"
## time-stamp-time-zone: "UTC0"
## time-stamp-end: "'; # UTC"
## End:
